#include <ctime>
#include <iostream>
#include <sstream>
#include "ComponentsHolder.h"
#include "SimulationParameters.h"
using namespace std;

// #include <vld.h>

void exploreFile(string filename);

void printFoo(ofstream &_file, vector<vector<int>> &data, int diffTime){
	int time=diffTime;
	for (int i = 0; data.size() > 0 && i < data[0].size(); i++)
		{
			_file << time << ",";
			time += diffTime;
			for (int j = 0; j < data.size(); j++)
			{
				_file << data[j][i]  << ",";
			}
			_file << endl;
	}
}


void ioHistogramPerLun()
{

	ifstream input(SimulationParameters::IOFileName + SimulationParameters::dataFiles[0]);
	
	int hour = 3600;
	int min = 50;
	int hourCounter = 0;
	int minCounter = 0;
	int t = 1000;
	map<int, vector<int>> lunsHour=map<int ,vector<int>>();
	map<int, vector<int>> lunsSecond=map<int ,vector<int>>();

	while(input.good())
	{

		char buf[101];
		input.getline(buf,100);
			
		string str = string(buf);
			
		if (str.size() == 0)
		{
			continue;
		}
		else
		{
			istringstream istr (str);
			string lineTime;
			istr >> lineTime;
			float curTime = atof(lineTime.c_str());
			istr >> lineTime;
			if (lineTime!="Read" && lineTime!="Write")
				continue;
			istr >> lineTime;
			istr >> lineTime;
			int lunID = hexaToInt(lineTime);
			
			map<int, vector<int>>::iterator it = lunsHour.find(lunID);
			if (it == lunsHour.end()){
				vector<int>hours = vector<int>(hour/3600);
				lunsHour.insert(pair<int, vector<int>>(lunID, hours));

				vector<int>minutes = vector<int>(min/50);
				lunsSecond.insert(pair<int, vector<int>>(lunID, minutes));
			}

			if (curTime >= hour) // reset second
			{
				map<int, vector<int>>::iterator it = lunsHour.begin();
				while(it!=lunsHour.end()){
					it->second.push_back(0);
					it++;
				}
				hour+=3600;
			}
			if (curTime >= min) // reset minute
			{
				map<int, vector<int>>::iterator it = lunsSecond.begin();
				while(it!=lunsSecond.end()){
					it->second.push_back(0);
					it++;
				}
				min += 50;
			}

			it = lunsHour.find(lunID);
			it->second[(hour/3600)-1]++;
			it = lunsSecond.find(lunID);
			it->second[(min/50)-1]++;

			if (curTime > t)
			{
				cout << "time is: " << t << endl;
				t += 1000;
			}
		}

	}

	ofstream hourFile("LunHistogramHour.csv");
	ofstream secondFile("LunHistogram50Second.csv");

	vector<vector<int>> HourData = vector<vector<int>>(lunsHour.size());
	vector<vector<int>> SecondData = vector<vector<int>>(lunsSecond.size());

	map<int, vector<int>>::iterator it = lunsSecond.begin();
	int i=0;

	secondFile<<",";
	while(it!=lunsSecond.end()){
		SecondData[i]=(it->second);
		secondFile << "LunID " << it->first << ",";
		it++;
		i++;
	}
	secondFile<<endl;

	i=0;
	hourFile<<",";
	it = lunsHour.begin();
	while(it!=lunsHour.end()){
		HourData[i]=(it->second);
		hourFile << "LunID " << it->first << ",";
		it++;
		i++;
	}
	hourFile<<endl;

	printFoo(hourFile, HourData, 3600);
	printFoo(secondFile, SecondData, 50);

	hourFile.close();
	secondFile.close();
	input.close();
}

void test()
{
	FlashDevice f(10000);
	map<string,int> extents;
	extents.insert(make_pair("a", 10));
	extents.insert(make_pair("b", 5));
	extents.insert(make_pair("c", 8));
	extents.insert(make_pair("d", 12));
	extents.insert(make_pair("e", 1));
	extents.insert(make_pair("f", 50));
	extents.insert(make_pair("g", 30));
	f.updateExtents(&extents);
	exit(1);
}

int main() 
{	

	
	// initialize all the parameters of the simulation
	SimulationParameters::init();

	EventsHandler* handler = ComponentsHolder::getInstance()->getEventsHandler();
	// initialize the simulation components and data
	handler->initSimulation();
	cout << "Start the simulation!!!" << endl;
	
	// start the simulation
	handler->startSimulation();
	ComponentsHolder::getInstance()->clear();
	
	return 0;
}

vector <string> split (string str, char delimiter)
{
	vector<string> ans;
	stringstream ss (str);
	int length = str.length();
	char *a=new char[str.size()+1];
	while (ss.good() )
	{
		ss.getline(a, length, delimiter);
		if (strlen(a)==0)
			continue;
		ans.push_back(string(a));
	}
	delete [] a;
	return ans;
}
int align( double size ) // size is in blocks
{
	int alignedSize = (int)(ceil((size / 2.0)/ SimulationParameters::CacheAlignment))*SimulationParameters::CacheAlignment;
	return alignedSize;
}
void exploreFile(string filename)
{
	cout << "start with file: " << filename << endl;
	ifstream in(filename);
	if (!in)
		return;
	string outPath = filename + " - summary.txt";
	ofstream out(outPath);
	out.close();
	/* 
		number of luns
		max lun size
		average lun size
		average number of io's per time slice
		max number of io's per time slice
		total read size per time slice
		total write size per time slice
	*/
	int timeSlice = 3600;
	int numberOfLuns;
	long maxLunSize = 0;
	double averageLunSize;
	double averageNumOfIOsPerTS;
	long maxNumOfIOsPerTS = 0;
		
	long totalNumOfIOs = 0;
	double totalReadSize = 0;
	double totalWriteSize = 0;
	
	long numOfIOsPerTS = 0;
	int simTime = timeSlice;
	int TSCount = 1;
	map<int,long> luns = map<int,long>(); // lunID,MaxOffset
	int x = 0;
	while (in.good())
	{
		char buf[1001];
		in.getline(buf,1000);
		string str = string(buf);
		if (str.size() == 0)
			continue;
		vector<string> parts=split(str, ' ');
		if (parts.size() != 6)
			continue;
		
		double time = atof(parts[0].c_str());
		string type = parts[1];
		string third = parts[2];
		int lunId = hexaToInt(parts[3]);
		long offset = atol(parts[4].c_str());
		int size = align(atoi(parts[5].c_str()));
		if (type != "Read" && type != "Write")
			continue;

		if (x++ == 1000000)
		{
			cout << "time is: " << time << endl;
			x = 0;
		}
		if (time > simTime) // next time slice
		{
			cout << "time: " << time << ", num of ios: " << numOfIOsPerTS << endl;
			maxNumOfIOsPerTS = MAX(maxNumOfIOsPerTS, numOfIOsPerTS);
			numOfIOsPerTS = 0;
			simTime += timeSlice;
			TSCount++;
		}
		numOfIOsPerTS++;
		totalNumOfIOs++;
		if (type == "Read")
			totalReadSize += size;
		else
			totalWriteSize += size;

		map<int,long>::iterator it = luns.find(lunId);
		if (it == luns.end())
			luns.insert(pair<int,long>(lunId,offset));
		else if (it->second > offset)
			it->second = offset;
	}
	in.close();
	// calc max lun size;

	long totalSystemSize = 0;
	for(map<int,long>::iterator it = luns.begin();it != luns.end(); it++)
	{
		maxLunSize = MAX(maxLunSize, it->second);
		totalSystemSize += it->second;
	}
	numberOfLuns = luns.size();
	double numOfTS = TSCount;
	if (numberOfLuns != 0)
	{

		averageLunSize = totalSystemSize / numberOfLuns;
		averageNumOfIOsPerTS = totalNumOfIOs / numOfTS;
		
		ofstream out(outPath);
		out << filename << " details:\n";
		out << "number of time slices: " << numOfTS << endl;
		out << "number of luns: " << numberOfLuns << endl;
		out << "max lun size(Blocks): " << maxLunSize << endl;
		out << "average lun size(Blocks): " << averageLunSize << endl;
		out << "average number of io's per time slice (K): " << averageNumOfIOsPerTS / 1000.0<< endl;
		out << "max number of io's per time slice: " << maxNumOfIOsPerTS << endl;
		out << "total read size (in GB): " << (totalReadSize / MB)<< endl;
		out << "total write size (in GB) : " << (totalWriteSize / MB)<< endl;
		out.close();
	}
	cout << "finished with file: " << filename << endl;	
	
}